Am Tag 2 und 3 haben wir uns in erster Linie mit Skalaren, also einzeln vorliegenden Daten, befaßt. Heute reden wir über Gruppen von Objekten, nämlich Listen und Arrays, und was Sie damit anstellen können. Sie lernen heute unter anderem,
<STDIN>
und wie Sie Daten in Listen einfügen und
Wenn man skalare Daten als einzelne Objekte definiert, kann man sich Listendaten als eine Sammlung mehrerer Objekte - genauer gesagt als Satz von Skalaren - vorstellen. Genau wie der Begriff Skalar sowohl Zahlen als auch Strings umfaßt, bezieht sich der Begriff Liste üblicherweise auf zwei spezielle Konstrukte: Arrays und Hashes. Wir werden heute über Arrays reden und morgen tiefer in die Hashes einsteigen.
Ein Array ist eine Sammlung von beliebig vielen Skalaren. Auf jedes einzelne Element kann zugegriffen werden, indem man sich auf die Nummer seiner Position im Array (seinen Index) bezieht. Array-Indizes beginnen in Perl bei 0. Zur Illustration zeigt Abbildung 4.1 ein einfaches Array mit ein paar Zahlen und Strings darin.
Abbildung 4.1: Anatomie eines Arrays
Arrays sind geordnet. Das bedeutet, sie haben ein erstes Element, ein letztes, und alle Elemente dazwischen stehen in einer bestimmten Reihenfolge. Man kann die Reihenfolge der Elemente durch Sortierung ändern oder die Elemente durchlaufen, eins nach dem andern, vom Anfang bis zum Ende.
Arrays werden in Arrayvariablen gespeichert, wie Skalare in Skalarvariablen
gespeichert werden. Eine Arrayvariable beginnt mit dem at-Zeichen @
. Abgesehen
von diesem ersten Zeichen gelten für Namen von Arrayvariablen die gleichen Regeln
wie für Skalarvariablen, nämlich die allgemeinen Variablennamensregeln:
Die Namen von Skalar- und Arrayvariablen können nicht miteinander in Konflikt
geraten. Die Skalarvariable $x
ist eine andere Variable als die Arrayvariable @x
.
Listendaten haben auch eine Rohform, die man (Überraschung!) eine Liste nennt. Eine Liste ist einfach eine Folge von Elementen. Sie können eine Liste einer Variablen zuweisen, eine Liste durchlaufen, um jedes ihrer Elemente auszugeben, sie in andere Listen verschachteln oder als Funktionsargument benutzen. Normalerweise werden Sie Listen zum Erstellen von Arrays und Hashes verwenden oder um Daten von einer Liste an eine oder mehrere andere zu übergeben. Arrays sind einfach eine bestimmte Form von Liste, aber im wesentlichen das gleiche, ja austauschbar.
Was macht man mit einem Perl-Array? Man legt es an (definiert es), weist es einer Arrayvariablen zu, packt Elemente hinein und liest oder nimmt sie wieder heraus, oder man zählt, wie lang das Array ist, das heißt wie viele Elemente es hat. Man kann noch eine ganze Menge mehr mit Arrays und ihren Daten anstellen, aber fangen wir mit den Grundlagen an.
Zur Definition von Perl-Arrays gibt es gar nicht viel zu sagen. Im Unterschied zu anderen Sprachen, in denen Arrays sorgfältig eingerichtet werden müssen, bevor man sie verwenden kann, erscheinen Arrays in Perl wie von Geisterhand dort, wo Sie sie brauchen, und sie wachsen und schrumpfen jederzeit und ganz von allein auf die richtige (also der aktuellen Anzahl der Elemente entsprechenden) Größe. In Perl- Arrays können Sie außerdem nicht nur jede Art von skalaren Daten speichern - Zahlen, alphanumerische Zeichen oder eine Mischung aus beiden -, sondern auch beliebig viele. Wie viele Elemente Sie in ein Array packen, bestimmen allein Sie und der Ihnen verfügbare Speicher.
Arrays können auch Referenzen enthalten, aber über die haben Sie noch nichts gelernt. Haben Sie Geduld, am Tag 19 wird alles klar.
Wie ich bereits erwähnt habe, bezieht sich der Begriff Liste auf einen unspezialisierten Satz von Daten, ein Array kann als eine Liste betrachtet werden, die in einer Arrayvariablen gespeichert ist. Sie können Listen überall verwenden, wo Arrays erwartet werden und umgekehrt.
Eine Liste erstellen Sie mit der Listensyntax: Tippen Sie die Elemente ein, setzen Sie Kommata dazwischen und alles zusammen in Klammern. Sie haben dann eine rohe Liste, mit der Sie ein Array oder einen Hash erstellen können, je nachdem, was für einer Variablen sie die Liste zuweisen. Betrachten wir jetzt ein paar Array-Beispiele. Hashes behandeln wir morgen.
Im folgenden Beispiel erstellen wir ein Array namens @zahlen
, das vier Elemente hat:
@zahlen = (1, 2, 3, 4);
Listen aus Strings sind genauso einfach:
@strings = ('Petersilie', 'Salbei', 'Rosmarin', 'Thymian');
Die Listensyntax kann jede beliebige Mixtur aus Strings, Zahlen, Skalarvariablen, Ausdrücken, die Skalare liefern, und anderen Elementen enthalten:
@kram = ('garbonzo', 3.145, $zaehler, 'Schokolade', 4 / 7, $a++);
Für eine leere Liste schreiben Sie einfach nichts zwischen die Klammern (nicht einmal ein Leerzeichen):
@nichts = ();
Sie können Listen ineinander verschachteln, aber diese Unterlisten werden im endgültigen Array nicht festgehalten. Alle Elemente werden in ein einziges Array aufgedröselt und leere Unterlisten dabei entfernt:
@kombination = (1, 4, (6, 7, 8), (), (5, 8, 3));
# ergibt (1, 4, 6, 7, 8, 5, 8, 3);
Ähnlich verhält sich Perl beim Verschachteln von Arrayvariablen. Alle Elemente in den Unterarrays werden zu einer einzigen größeren Liste verkettet:
@kombination2= (@zahlen, @strings);
# ergibt (1, 2, 3, 4, 'Petersilie', 'Salbei', 'Rosmarin', 'Thymian')
@zahlen = (@zahlen, 5); # ergibt (1, 2, 3, 4, 5);
Sagen wir, Sie definieren ein Array aus Ein-Wort-Strings, zum Beispiel Wochentagen
oder eine Namensliste. Ein sehr gebräuchlicher Perl-Trick zum Erstellen von Ein-Wort-
Arrays ist der Einsatz der speziellen qw
-Syntax (von »quote word«, »zitiere Wort«). Die
qw
-Syntax erspart Ihnen das Eintippen der Anführungszeichen und Kommata und
macht das Array um einiges lesbarer:
@monate = qw(
Januar Februar Maerz
April Mai Juni
Juli August September
Oktober November Dezember
);
Sie brauchen eine Liste aller Zahlen zwischen 1 und 1000? Hierfür alle Zahlen einzeln einzutippen, wäre schändliche Zeitverschwendung. Irgendeine Schleife, die eine Zahl nach der anderen hinzufügt, wäre da schon einfacher, aber immer noch ziemlich kludgy. (Ein kludge ist alter Freak-Jargon für eine nicht so optimale Lösung eines Problems. Einige irregeführte Einzelpersonen würden argumentieren, dass Perl selbst ein kludge ist. Doch das ignorieren wir mit einem Lächeln.)
In welcher Situation auch immer Sie denken: »Es muss einen einfachen Weg geben,
das hinzukriegen«, stehen die Chancen wirklich gut, dass es einen solchen einfachen
Weg in Perl auch gibt. In diesem Fall ist es der Bereichsoperator (..
), auch Range-
Operator genannt. Um ein Array mit den Zahlen von 1 bis 1000 zu erstellen,
schreiben Sie einfach:
@vieleZahlen = (1 .. 1000);
Das war's, Sie sind fertig. Das Array @vieleZahlen
enthält jetzt 1000 Elemente, von 1
bis 1000. Der Range-Operator arbeitet so, dass er vom linken Operanden jeweils um
1 bis zum rechten Operanden hochzählt (runterzählen kann man damit nicht).
Der Bereichsoperator funktioniert auch mit Buchstaben:
@alphabet = ('a' .. 'z'); # enthält 26 Elemente
Wir werden am Tag 6 noch einmal auf den Bereichsoperator zurückkommen, wenn wir uns mit Iterationen befassen.
Bis jetzt haben wir die Listensyntax auf der rechten Seite des Zuweisungsausdrucks verwendet, um eine Liste zu erstellen und sie einer Arrayvariablen zuzuweisen. Sie können auf die linke Seite einer Zuweisung aber auch eine Liste von Variablenreferenzen stellen. Perl wird diesen Variablen dann auf Grundlage der Liste rechts Werte zuweisen.
Nehmen Sie zum Beispiel folgenden Ausdruck:
($a, $b) = (1, 2);
Auf beiden Seiten der Zuweisung haben wir hier eine Liste. Doch nach wie vor ist
links die Seite, auf der Variablen gespeichert werden. Perl weist die Werte der rechten
Liste den Variablen in der linken Liste zu, und zwar in der Reihenfolge, in der sie
auftreten. So erhält $a
den Wert 1
und $b
den Wert 2
. Das ist natürlich genau das
gleiche, als würden Sie die Werte in getrennte Zeilen schreiben, aber auf diese Art
können Sie bequemer mehreren Variablen Werte zuweisen. Beachten Sie, dass die
Zuweisungen parallel ablaufen, so dass man auch schreiben kann:
($x, $y) = ($y, $x);
Dieses Beispiel vertauscht die Werte von $x
und $y
. Nichts passiert vor dem anderen,
deswegen wird es wie gewünscht funktionieren.
Wenn auf beiden Seiten eines Zuweisungsoperators Listen stehen, gilt die Regel, dass jede Variable auf der linken Seite einen Wert aus der rechten Seiten erhält. Wenn es links mehr Variablen als rechts Werte gibt, dann erhalten die übrigen Variablen den undefinierten Wert. Gibt es mehr Werte als Variablen, werden die übrigbleibenden Werte ignoriert.
Sie können auf die rechte Seite auch Arrayvariablen stellen. Das Array wird dann in seine Elemente zerlegt, und die Werte werden wie im Falle einer normalen Listensyntax zugewiesen:
($a, $b) = @zahlen;
Sie können Arrays sogar auf beiden Seiten in Listen verschachteln - sie werden in ihre Elemente aufgedröselt und nach den obigen Regeln zugewiesen - mit einer Ausnahme:
($a, @mehr) = (10, 11, 12, 13, 14);
In diesem Beispiel erhält $a
den Wert 10
und @mehr
erhält die Liste (11, 12, 13, 14
).
Arrayvariablen auf der linken Seite einer Listenzuweisung sind gefräßig - das heißt,
sie speichern alle noch folgenden Elemente der Liste auf der rechten Seite der
Zuweisung. Das ist wichtig, wenn Sie zum Beispiel folgendes Beispiel betrachten:
($a, @mehr, $b) = (10, 11, 12, 13, 14);
In diesem Fall erhält $a
den Wert 10
, @mehr die Liste (11, 12, 13, 14
) und $b
den
undefinierten Wert. Weil das Array alle verbleibenden Werte auf der rechten Seite
»auffrißt«, bleibt keiner mehr übrig, der $b
zugewiesen werden könnte.
Nun haben Sie also ein Array, vielleicht mit ein paar in Listenschreibweise vorgegebenen Elementen, oder einfach ein leeres Array, das Sie später mit Werten (beispielsweise Benutzereingaben) füllen möchten.
Um auf ein Array-Element zuzugreifen, verwenden Sie die []
-Syntax, die Sie vielleicht
aus anderen Sprachen kennen: Der Index des Elements, auf das Sie zugreifen wollen,
folgt in eckigen Klammern auf den Arraynamen:
$zahlen[4];
Dieses Beispiel liefert Ihnen den Wert des fünften Elements im Array @zahlen
. (Sie
erinnern sich, der Index startet bei 0.) Das folgende Beispiel ändert den Wert des
ersten Elements in 10
:
$zahlen[0] = 10;
Halten Sie kurz an, und betrachten Sie diese Syntax etwas genauer. Kommt sie Ihnen
komisch vor? Sie sollte. Wir beziehen uns hier auf das fünfte Element in einem Array
namens @zahlen
, aber wir benutzen etwas, das wie eine Skalarvariable aussieht. Das
ist kein Druckfehler - so arbeitet die Syntax. Sie verwenden @arrayname,
um sich auf
das ganze Array zu beziehen, und $arrayname[index]
, um auf ein einzelnes Element
innerhalb dieses Arrays zuzugreifen.
Das bedeutet nicht, dass Sie $arrayname
nicht verwenden können, um auf einen
normalen Skalar zu verweisen. $arrayname
, $arrayname[index]
und @arrayname
sind
ganz verschiedene Dinge. Am besten merkt man sich das, indem man sich daran
erinnert, dass die Elemente von Arrays ja Skalare sind und man deswegen eine
Skalarvariable braucht, um auf sie zuzugreifen, auch wenn sie innerhalb eines Arrays
stehen (die Perl-Warnungen geben Ihnen Bescheid, falls Sie das vergessen).
Array-Indizes beginnen bei 0 wie in anderen Sprachen auch, und ein Index kann nur
eine ganze Zahl (ein Integer) sein. So bezieht sich $arrayname[0]
auf das erste
Element in einem Array, $arrayname[1]
auf das zweite und so weiter. Sie müssen
nicht explizit eine Zahl als Index verwenden, Sie können auch eine Variable oder
jeden anderen Ausdruck dafür nehmen, der zu einer Zahl ausgewertet wird:
$array[$zaehler]; # das Element an Position $zaehler;
Was passiert, wenn Sie versuchen, auf ein Element zuzugreifen, das gar nicht existiert
- zum Beispiel auf Position 5 in einem dreielementigen Array? Wenn Sie die
Warnungen aktiviert haben, erhalten Sie eine Fehlermeldung. Anderenfalls erhalten
Sie einen undefinierten Wert (0
oder ""
, je nach Kontext).
Wenn Sie hinter dem letzten Element eines Arrays ein weiteres Element anfügen, wird Perl das Array entsprechend vergrößern, das Element hinzufügen und eventuell dazwischenliegenden Elementen den undefinierten Wert zuweisen. Zum Beispiel:
@t = (1, 2, 3);
$t[5] = 10;
Das Array @t
enthält nach diesen beiden Zeilen die Elemente (1, 2, 3,
undefiniert, undefiniert, 10)
. Wenn Sie versuchen, auf die undefinierten Werte
in der Mitte zuzugreifen, wird Perl Sie warnen (vorausgesetzt, Sie haben die
Warnungen angeschaltet).
Möchten Sie sichergehen, dass Sie es nur mit definierten Werten zu tun haben,
können Sie Ihre Werte mit der Funktion defined
überprüfen:
if (!defined $array[$index]) {
print "Element $index ist undefiniert.\n";
}
Alternativ dazu können Sie ein Array-Element »leeren«, indem Sie es mit der Funktion
undef
auf den undefinierten Wert setzen:
if (defined $array[$index]) {
undef($array[$index]);
}
Beachten Sie, dass das Array-Element, das Sie mit undef
undefiniert gemacht haben,
sich nach wie vor im Array befindet, nur dass es jetzt den undefinierten Wert hat. Um
wirklich ein Element aus einem Array zu entfernen, müssen Sie es explizit löschen,
indem Sie das Array neu konstruieren.
Die undef
-Funktion kann übrigens überall eingesetzt werden, nicht nur in Arrays, um
einer beliebigen Variablen den undefinierten Wert zuzuweisen. Man kann sie auch
ohne jegliches Argument verwenden, dann gibt sie einfach den undefinierten Wert
zurück, zum Beispiel:
@loch_in_der_mitte = (1, undef, undef, undef, 5);
Weil man auf diese Art so leicht einen undefinierten Wert erhält, bezeichnet man ihn
üblicherweise einfach mit undef
(und für den Rest des Buches werde ich genau das
tun).
Weil Arrays in Perl jede beliebige Größe haben können, brauchen Sie eine Methode,
das Ende eines Arrays zu finden. Schließlich muss zum Beispiel eine Schleife, die die
Inhalte des Arrays durchläuft, wissen, wann Ende ist. Bequemerweise hat Perl hierfür
eine eigene Syntax, die Ihnen die Indexnummer des letzten Array-Elements liefert:
$#array
. Mit dem Index des letzten Elements könnten Sie eine einfache Schleife
bauen, die vom Index 0 bis zum letzten läuft. So könnten Sie zum Beispiel mit einem
Konstrukt wie dem folgenden alle Elemente eines Arrays zeilenweise ausgeben:
$i = 0;
while ($i <= $#array) {
print $array[$i++], "\n";
}
Für Operationen dieser Art verwendet man üblicherweise eine
for
- oderforeach
-Schleife statt einerwhile
-Schleife. Aber da Sie bis jetzt nurwhile
-Schleifen gesehen haben, wollte ich hier keine andere nehmen. Wir werden unsforeach
später in dieser Lektion noch ansehen.
Wenn Sie den Wert von $#array
verändern, dann vergrößern bzw. verkleinern Sie das
Array. Setzen Sie $#array
auf einen größeren als den aktuellen Wert, erhalten alle
neu hinzugekommenen Elemente den undefinierten Wert. Wenn Sie den Wert von
$#array
verkleinern, werden alle Elemente am Ende des Arrays verworfen.
Beachten Sie, dass man mit der Syntax $#array
nicht die Länge des Arrays (oder die
Anzahl der enthaltenen Werte) herausfindet. Weil Array-Indizes bei 0 beginnen, liefert
Ihnen $#array
einen Wert, der um eins kleiner ist als die Anzahl von Elementen im
Array. Wie man die Länge eines Arrays ermittelt, ist Thema des nächsten Abschnitts.
Um die Anzahl der Elemente in einem Array zu ermitteln, verwenden Sie folgende Anweisung:
$anzahl_elemente = @array;
Denken Sie darüber jetzt nicht lange nach, merken Sie sich einfach: Um die Anzahl der Elemente in einem Array zu erhalten, verwendet man eine Skalarvariable und weist ihr eine Arrayvariable zu. Ich werde später erklären, warum das funktioniert (im Abschnitt »Listen- und Skalarkontexte«).
In anderen Sprachen müssen Sie, wenn Sie die Inhalte eines Arrays sortieren
möchten, vielleicht eine eigene Sortierroutine schreiben. Nicht so in Perl, da ist schon
eine eingebaut. Zum Sortieren eines Arrays brauchen Sie lediglich die Funktion sort
:
@geordnete_zahlen = sort @zahlen;
Diese Zuweisung sortiert das Array @zahlen
und weist die neue Liste dann der
Variablen @geordnete_zahlen
zu. Das @zahlen-Array bleibt unverändert, also
unsortiert.
Diese besonders simple sort
-Variante sortiert die Inhalte von @zahlen
in ASCII-
Reihenfolge - das heißt, dass 5543
im Array vor 94
steht (weil 5 vor 9 kommt). Um
das Array in numerischer Reihenfolge zu sortieren, setzen Sie nach sort
einen
speziellen Vergleichsausdruck:
@geordnete_zahlen = sort { $a <=> $b } @zahlen;
Der Teil in den geschweiften Klammern legt fest, wie sort
das Array mit Hilfe des
Vergleichsoperators <=>
sortiert. Wir werden an Tag 8 zum Anpassen von sort
-
Routinen kommen. Jetzt können Sie sich erst einmal folgendes merken: Um ein
Array nach Zahlen zu sortieren, verwenden Sie sort
mit einem Vergleich. Um ein
Array alphabetisch zu sortieren, nehmen Sie die Kurzform ohne den Vergleich.
Vorhin habe ich Ihnen in einem Beispiel eine while
-Schleife gezeigt, die alle Elemente
eines Arrays durchläuft, vom ersten Element zum letzten. Viel gebräuchlicher für eine
solche Iteration über ein Array, wie ein solcher Durchlauf auch genannt wird, ist
allerdings die Verwendung einer foreach
-Schleife wie hier:
foreach $x (@liste) {
# mache etwas mit dem jeweiligen Element
}
Die foreach
-Schleife wird für jedes Element der Liste innerhalb der Klammern einmal
ausgeführt (hier ist es das Array @liste
, aber es könnte auch eine rohe Liste, ein
Bereich oder alles andere sein, was Ihnen als Ergebnis eine Liste liefert (zum Beispiel
die sort
-Funktion.) Bei jedem Listenelement wird der Wert dieses Elements einer
Skalarvariablen (hier $x
) zugewiesen und der Code zwischen den geschweiften
Klammern ausgeführt. Sie könnten zum Beispiel eine foreach
-Schleife verwenden,
um jedes Listenelement auszugeben, sie alle zusammenzufügen oder sie auf den Wert
undef
zu überprüfen.
Mehr über foreach
erfahren Sie am Tag 6 und morgen, wenn wir über Hashes reden.
Erinnern Sie sich an das simple Statistikskript von gestern? Man sollte nacheinander Zahlen eingeben, das Skript berechnete dann Anzahl, Summe und Durchschnitt. Lassen Sie uns dieses Skript heute etwas verändern, so dass es die eingegebenen Zahlen in ein Array speichert. Haben wir nämlich die Zahlen auch nach der Eingabe noch parat, können wir mehr mit ihnen anstellen - sie zum Beispiel sortieren oder den Median herausfinden (ein Zahlenwert, der die Zahlenliste so halbiert, dass die Hälfte der Zahlen kleiner und die andere Hälfte größer als dieser Wert ist).
Die neue Version des Skripts könnte zum Beispiel so ablaufen:
% mehrstats.pl
Bitte eine Zahl eingeben: 4
Bitte eine Zahl eingeben: 5
Bitte eine Zahl eingeben: 3
(Und so weiter, aus Platzgründen hier nicht aufgeführt.)
Bitte eine Zahl eingeben: 47
Bitte eine Zahl eingeben: 548
Bitte eine Zahl eingeben: 54
Bitte eine Zahl eingeben: 5485
Bitte eine Zahl eingeben:
Anzahl der eingegebenen Zahlen: 49
Summe der eingegebenen Zahlen: 10430
Hoechste Zahl: 5485
Niedrigste Zahl: 2
Durchschnitt: 212.86
Median: 45
%
Schon auf den ersten Blick erkennt man, worin sich dieses Statistikskript von der gestrigen Version unterscheidet:
Im Code finden wir einen weiteren bedeutenden Unterschied zwischen dieser und der letzten Version: Wir speichern die Eingabedaten diesmal in ein Array, anstatt sie einfach zu verwerfen. (Der Eingabevorgang hat sich nicht verändert: Wenn Sie das Skript ausführen, beenden Sie die Dateneingabe nach wie vor mit einer leeren Zeile). Listing 4.1 zeigt den Code für das neue Skript.
Listing 4.1: Das Skript mehrstats.pl
1: #!/usr/bin/perl -w
2:
3: $input = ''; # Benutzereingabe
4: @nums = (); # Zahlen-Array
5: $count = 0; # Zaehler
6: $sum = 0; # Summe
7: $avg = 0; # Durchschnitt
8: $med = 0; # Median
9:
10: while () {
11: print 'Bitte eine Zahl eingeben: ';
12: chomp ($input = <STDIN>);
13: if ($input ne '') {
14: $nums[$count] = $input;
15: $count++;
16: $sum += $input;
17: }
18: else { last; }
19: }
20:
21: @nums = sort { $a <=> $b } @nums;
22: $avg = $sum / $count;
23: $med = $nums[$count /2];
24:
25: print "\nAnzahl der eingegebenen Zahlen: $count\n";
26: print "Summe der eingegebenen Zahlen: $sum\n";
27: print "Hoechste Zahl: $nums[$#nums]\n";
28: print "Niedrigste Zahl: $nums[0]\n";
29: printf("Durchschnitt: %.2f\n", $avg);
30: print "Median: $med\n";
Diese Version des Statistikskripts besteht aus vier Abschnitten: Initialisierung, Dateneingabe, Sortierung und Statistikberechnung und schließlich Ausgabe der Ergebnisse.
Der Initialisierungsabschnitt, Zeile 3 bis 7, ist derselbe wie im vorherigen Skript, außer
dass wir zwei Variablen hinzugefügt haben: eine Arrayvariable (@nums
) in Zeile 4 zum
Speichern der numerischen Eingaben und die $med
-Variable in Zeile 8 für den
Medianwert. Wie bei anderen Variablen bräuchten wir @nums
eigentlich nicht
initialisieren, aber es sieht nett aus und verschafft uns am Anfang des Skripts einen
Überblick über alle verwendeten Variablen.
Die Zeilen 10 bis 19 sind die neue while
-Schleife für die Dateneingabe. Wenn Sie
diese Version mit der von gestern vergleichen, werden Sie bemerken, dass hier
eigentlich nicht viel Neues steht. Wir lesen immer noch eine Zahl pro Zeile ein,
inkrementieren den Zähler $count
und aktualisieren mit jeder Zahl die Variable $sum
(und wir überprüfen auch immer noch nicht, ob die Eingaben wirklich nur Zahlen
enthalten). Der einzige Unterschied ist die Zeile 14, in der wir bei jedem
Schleifendurchlauf die neue Zahl dem Array @nums
hinzufügen. Die $count
-Variable
erfüllt hier einen doppelten Zweck: Sie ist nicht nur Zähler der Eingaben, sondern
auch Array-Index (beachten Sie, dass wir das Array korrekt bei 0
starten, weil wir
$count
erst danach, in Zeile 15, erhöhen.
Sind alle Eingaben gemacht, geht es weiter zu Zeile 21, wo wir das @nums
-Array mit
der numerischen sort
-Routine sortieren, die ich vorhin beschrieben habe. Beachten
Sie, dass wir die Variablen $a
oder $b
nicht deklarieren brauchen - diese Variablen
sind lokale Variablen von sort
und fallen unter den Tisch, sobald die Sortierung
vollständig ist. Zeile 22 berechnet den Durchschnitt wie in unserer vorigen Version
des Skripts, und Zeile 23 errechnet den Median, also den Wert in der Mitte einer
sortierten Liste. Dafür müssen wir lediglich den Wert von $count
durch zwei teilen und
das Ergebnis dann als Array-Index angeben (im Falle einer ungeraden Anzahl von
Elementen ist das Ergebnis natürlich keine ganze Zahl, aber dann schneidet Perl die
Nachkommastellen einfach ab).
Damit kommen wir zur Auswertung in den Zeilen 25 bis 30. Zähler, Summe und Durchschnitt sind dieselben wie vorher, aber jetzt haben wir auch Höchstwert, kleinsten und Zentralwert hinzugefügt. Maximum und Minimum sind einfach: Da unser Array ja sortiert ist, brauchen wir nicht einmal etwas berechnen, wir nehmen nur das erste und das letzte Element des Arrays. Und da wir den Median schon seit Zeile 23 kennen, müssen wir ihn nur noch ausgeben wie alle anderen Werte.
Bevor wir die Arrays hinter uns lassen, möchte ich eine kleine Pause einlegen und das Thema Kontext behandeln. Kontext bezeichnet in Perl den Effekt, dass das Verhalten von Daten davon abhängt, was Sie mit ihnen zu machen versuchen. Viele an und für sich identische Daten »benehmen« sich je nach Kontext ganz unterschiedlich. Kontext kann sehr verwirrend sein, und wenn Sie damit durcheinandergeraten, können Sie Ergebnisse erhalten, die absolut sinnlos zu sein scheinen (oder Teile von Skripten anderer Programmierer produzieren scheinbar aus dem Nichts Resultate). Wenn Sie jedoch erst einmal verstanden haben, was Kontext in Perl bedeutet, und wissen, wie verschiedene Operationen damit arbeiten, können Sie auch komplexe Aufgaben sehr schnell erledigen, die in anderen Sprachen viele Zeilen Code erfordern würden.
Wenn Sie ein erfahrener Programmierer sind und das Buch gerade nach Wichtigem durchsuchen, halten Sie genau hier an. Nehmen Sie sich die Zeit, diesen Abschnitt aufmerksam zu lesen und sicherzugehen, dass Sie es verstanden haben. Kontext kann sowohl Anfänger als auch erfahrene Perl-Programmierer in den Wahnsinn treiben.
Was also bedeutet Kontext? Lassen Sie uns eine Analogie aus dem Deutschen verwenden: Nehmen Sie das Wort meinen. Definieren Sie es, in 25 Wörtern oder weniger.
Was ist die Definition des Worts meinen? Die richtige Antwort ist eigentlich: »Kommt drauf an, wie man es verwendet.« In dem Satz »Sie meinen nicht mich, sondern meinen Bruder« ist meinen einmal ein Verb (»Sie meinen«) und einmal ein Pronomen (»meinen Bruder«). »Sie meinen meinen Bruder« - dasselbe Wort, dieselbe Schreibweise, aber ganz verschiedene Bedeutungen, abhängig vom Zusammenhang - vom Kontext.
»Okay«, werden Sie sagen, »aber was hat das mit Perl zu tun?« Nehmen Sie den
simplen Perl-Ausdruck 5 + 4
. Wozu wird dieser Ausdruck ausgewertet?
»Na, 9
«, sagen Sie und fragen sich, ob da ein Haken ist. Darauf können Sie wetten, es
gibt einen: Was ist, wenn Sie diesen Ausdruck in eine Bedingung stellen, zum Beispiel
so:
if (5 + 4) { ... }
Wozu wird 5 + 4
jetzt ausgewertet? Gut, zuerst immer noch zu 9
. Aber weil wir es hier
als Bedingung verwenden, wird es auch als wahr ausgewertet (Sie erinnern sich, nur 0
und ""
sind falsch). Im Kontext einer Bedingung - im Perl-Jargon Boolescher Kontext
genannt - ist das Ergebnis von 5 + 4
ein Boolescher Wert, keine Zahl. Die if
-
Konstruktion erwartet als Ergebnis wahr oder falsch; Perl erkennt das und liefert wie
(vom if
, vielleicht gar nicht von Ihnen) gewünscht.
Auch die automatische Konvertierung zwischen Zahlen und Strings basiert auf Kontext. Bei Berechnungen erwartet Perl numerische Operanden (einen numerischen Kontext), deswegen konvertiert es alle Strings zu Zahlen. Stringoperatoren verlangen Stringkontext, und deshalb wandelt Perl numerische Werte in Strings um.
Numerischer, String- und Boolescher Kontext sind Formen des allgemeinen skalaren Kontexts. Um die Unterscheidung dieser drei brauchen Sie sich üblicherweise keine Sorgen zu machen, da Perl sie normalerweise erkennt und für Sie berücksichtigt.
Komplizierter wird es bei der Unterscheidung von Skalaren und Listen. Jede Operation in Perl wird entweder in skalarem oder in Listenkontext ausgewertet. Manche Perl-Operationen verlangen eindeutig skalaren, andere Listenkontext. Die Verwendung des falschen Datentyps würde bei diesen Operationen zu Fehlermeldungen führen. Doch eine dritte Sorte von Operationen kann sowohl in skalarem als auch in Listenkontext ausgewertet werden und sich in einem Kontext anders verhalten als in dem anderen. Und, um die Sache noch komplizierter zu machen, es gibt keine Standardregeln, wie Listen sich in skalarem Kontext verhalten und umgekehrt. Jede Operation hat ihre eigenen Regeln, und die müssen Sie sich merken oder nachsehen. Sobald Sie also mit einer Mischung aus Listen und Skalaren zu tun haben, sollten Sie sich für jede Operation in Perl drei Fragen stellen:
Ich zeige Ihnen jetzt einige wenige Beispiele dieser »dritten Art«, in der Kontext so wichtig ist. Doch das Thema ist mit dieser Lektion nicht abgehakt, sondern fordert weiteren Fleiß Ihrerseits. Der Kontext spielt in Perl und in allen folgenden Lektionen so gut wie immer eine Rolle. Behalten Sie diese drei Fragen im Kopf und die Perl-Dokumentation griffbereit, dann ist es gar nicht so schwierig, wie es im Moment vielleicht scheint.
Ein Beispiel haben Sie bereits gesehen, in dem der Kontext entscheidend war. Erinnern Sie sich, wie man die Länge eines Arrays herausfindet?
$anzahl_elemente = @array;
Ich habe Ihnen dazu gesagt, Sie sollen sich die Syntax einfach merken und sich keine
Sorgen darum machen. Jetzt betrachten wir die Syntax genauer, denn es handelt sich
um ein klassisches Beispiel, wie verwirrend Kontext in Perl sein kann. Die Variable
$anzahl_elemente
ist skalar, so dass die Zuweisung an diese Variable in skalarem
Kontext ausgewertet wird - das heißt, auf der rechten Seite des =
-Operators wird ein
skalarer Wert erwartet (das ist die Antwort auf Ihre erste Frage: In welchem Kontext
befinde ich mich? - In skalarem Kontext).
Auf der rechten Seite haben Sie eine Liste (das ist die Antwort auf die zweite Frage:
Was für einen Datentyp benutze ich?). Also haben wir hier eine Liste in skalarem
Kontext. Jetzt bleibt nur noch die Frage: »Was passiert mit einer Liste in diesem
Kontext?« In diesem Fall wird die Anzahl von Elementen im Array @array
der
Variablen $anzahl_elemente
zugewiesen. Wird eine Arrayvariable in skalarem
Zuweisungskontext ausgewertet, liefert sie die Anzahl der Listenelemente.
Betrachten Sie diesen Vorgang nicht als Konvertierung der Liste in einen Skalar, denn das ist es nicht. Es findet keine Konvertierung statt. Die Liste benimmt sich in diesem Kontext einfach anders als sonst.
Überall, wo ein skalarer Wert erwartet wird und Sie eine Arrayvariable einfügen, erhalten Sie die Anzahl der Elemente dieses Arrays - zum Beispiel in jedem der folgenden Ausdrücke:
$med = $array[@array / 2]; # Median, erinnern Sie sich?
$total = @array1 + @array2 + @array3;
if (@array > 10) { ... }
while (@array) { ... }
Wenn Ihnen das verwirrend oder zu kryptisch aussieht, können Sie natürlich immer
die simple Skalarzuweisung auf die eine und die Arrayvariable auf die andere Seite
stellen. Oder wenn Sie die Länge des Arrays nur interessiert, weil Sie es mit einer
Schleife durchlaufen möchten, können Sie die Frage nach der Anzahl der Elemente
auch komplett umgehen und statt dessen $#array
als Stoppwert verwenden.
Zuweisungen sind der häufigste Fall, in dem der Kontext wichtig wird - oder
zumindest ist Kontext hier am einfachsten zu erklären. Der Kontext auf der linken und
der rechten Seite des Zuweisungsoperators kann passen (Skalar =
Skalar, Liste =
Liste), oder eben nicht (Skalar =
Liste, Liste =
Skalar). In diesem Abschnitt werde ich
ein paar der einfachen Regeln durchgehen, wie Zuweisungen mit Kontext umgehen.
Lassen Sie uns mit den einfachen Fällen anfangen, in denen der Kontext paßt. Die haben Sie bereits alle kennengelernt. Skalar an Skalar bedeutet, ein einzelnes Objekt an ein einzelnes Objekt zuzuweisen, wobei Strings und Zahlen gegebenenfalls konvertiert werden:
$x = 'foo'; # Skalarvariable, skalarer Wert
Auch Listen-an-Listen-Zuweisungen kennen Sie schon, sowohl mit Arrayvariablen auf der linken und Listensyntax auf der rechten Seite des Zuweisungsoperators als auch mit verschachtelten Listen:
@zahlen = (10, 20, 30);
($x, $y, $z) = @zahlen;
($a, @zahlen2) = @zahlen;
Jetzt wollen wir uns die Fälle ansehen, in denen der Kontext nicht paßt. Sagen wir, Sie versuchen, einen Skalar in einem Listenkontext zuzuweisen, wie in diesen Beispielen:
@array = 3.145;
($a, $b) = $c;
Das ist einfach! In diesen Fällen wird der skalare Wert rechts einfach in eine Liste
umgewandelt und dann unter Verwendung der Listenzuweisungsregeln zugewiesen.
Danach ist @array
eine Liste mit einem einzigen Wert, 3.145
, $a
hat den Wert von $c
,
und $b
ist undefiniert.
Der vertrackteste Fall ist die Zuweisung einer Liste auf der rechten Seite an einen Skalar auf der linken. Sie haben gelernt, was passiert, wenn Sie eine Arrayvariable einer Skalarvariablen zuweisen:
$anzahl_elemente = @array;
Das funktioniert mit jedem Array:
$anzahl_elemente = sort @array;
Doch es funktioniert nur bei Arrays. Für ganz normale Listensyntax, also durch Kommas aufgeteilte Listen, ist die Regel anders:
$x = (2, 4, 8, 16);
In diesem Fall werden - wie beim Komma-Operator in C - alle Werte in der Liste,
außer dem letzten ignoriert. Der Wert von $x
wird hier 16
. (Das ist etwas anderes als
die Zuweisung derselben Liste an ($x
) - eine Listen-an-Listen-Zuweisung würde beim
ersten Element, 2
, beginnen und unbenutzte Elemente verwerfen.)
Das Wichtigste, was Sie sich merken sollten, ist, dass es keine allgemeine Regel gibt, wie eine Liste sich in skalarem Kontext verhält - Sie müssen die einzelnen Regeln kennen oder greifbar haben. Behalten Sie die drei Fragen im Kopf; dann dürften Sie keine Schwierigkeiten haben.
Es gibt noch ein paar andere Kontext-Situationen, die einer Betrachtung wert sind - und die Sie beim Arbeiten mit Listen und Skalaren beachten sollten.
Zuerst ist da der Boolesche Kontext, in dem eine Variable auf ihren Wahrheitswert
überprüft wird (wie zum Beispiel im Bedingungsausdruck von if
oder while
). Sie
haben schon gelernt, dass ein skalarer Wert in Booleschem Kontext wahr ist, wenn er
irgendeinen Wert außer ""
, 0
oder undef
hat.
Für Listen in Booleschem Kontext gilt eine ähnliche Regel - eine Liste mit irgendwelchen Elementen, einschließlich der undefinierten, ist in Booleschem Kontext wahr. Eine leere Liste ist falsch.
Zum zweiten ist der Kontext häufig sehr wichtig bei Funktionen. Viele Funktionen
nehmen ihre Argumente als Liste entgegen und werten sie im Listenkontext aus.
Wenn Sie Klammern um die Funktionsargumente setzen, wie wir gestern besprochen
haben, ist das kein Problem - Sie übergeben der Funktion eine Liste mit Argumenten.
Setzen Sie die Klammern aber nicht, versucht Perl, aus den gegebenen Argumenten
selbst eine Liste zu bauen. Wenn diese Argumente aber Listen oder eingeklammerte
Ausdrücke enthalten, kann Perl durcheinanderkommen. Betrachten wir ein Beispiel
mit einer der Funktionen, die als Argument eine Liste erwarten, nämlich print
:
priNT 4 + 5, 6, 'foo';
Hier werden die auszugebenden Argumente ausgewertet, als wären sie die Liste (9,
6, 'foo')
. Im folgenden Fall ist es allerdings anders:
print (4 + 5), 6, 'foo';
Wegen der Klammern nimmt Perl an, dass der Ausdruck 4 + 5
das einzige Argument
ist, und versteht nicht, was die 6
und das 'foo'
am Ende sollen. Aktivierte Perl-
Warnungen würden diesen Fehler abfangen (und sich über die Verwendung von
Konstanten in leerem Kontext beschweren). In solch einem Fall ist es am besten, die
ganze Argumentenliste einzuklammern, damit nichts zweideutig ist:
print ((4 + 5), 6, 'foo');
Meistens kann Perl erkennen, ob Klammern einen Funktionsaufruf, einen Ausdruck oder eine Liste meinen. In den meisten übrigen Fällen weisen die Warnungen Sie auf die Zweideutigkeiten hin. Aber achten Sie auf diese Unterschiede und den Kontext, wenn Sie Perl-Skripts schreiben.
»Und was ist, wenn ich wirklich mal eine Liste im Skalarkontext verwenden möchte?«
fragen Sie sich jetzt vielleicht. Keine Angst, Sie brauchen dafür keine langen Umwege
(zum Beispiel eine temporäre Skalarvariable, nur um die Liste in skalaren Kontext zu
bekommen). Mit der Funktion scalar
können Sie eine Liste immer dazu zwingen, in
skalarem Kontext ausgewertet zu werden. Nehmen Sie zum Beispiel die beiden
folgenden Anweisungen:
print @zahlen;
print scalar(@zahlen);
Die print
-Funktion wertet ihre Argumente im Listenkontext aus (deshalb können Sie
mehrere durch Kommata getrennte Argumente angeben). Die erste dieser beiden
Anweisungen expandiert nun das Array @zahlen
in Listenkontext und gibt die Werte
des Arrays aus. Die zweite wertet @zahlen
in skalarem Kontext aus und liefert Ihnen in
diesem Fall die Anzahl der Elemente von @zahlen
.
Wie gestern betrachten wir zum Abschluß der Lektion einige Eingabe-/Ausgabe- Themen, diesmal mit besonderem Augenmerk auf den Listenkontext:
Gestern habe ich Ihnen gezeigt, wie man mit <STDIN>
Daten von der Standardeingabe
liest. Bis jetzt haben wir <STDIN>
wie folgt verwendet:
chomp($in = <STDIN>);
Bei genauem Betrachten erkennen Sie, dass wir <STDIN>
hier in skalarem Kontext
verwenden. Wie viele andere Perl-Operationen verhält sich auch der Eingabeoperator
<>
in Listenkontext anders als in skalarem.
In Skalarkontext liest <STDIN>
eine Eingabezeile bis zum Zeilenvorschub. In
Listenkontext aber liest <STDIN>
immer weiter und speichert jede Zeile als ein
Element in eine Liste, und zwar so lange, bis es zu einem Dateiende (end-of-file-
)Zeichen kommt:
@liste = <STDIN>;
Nun werden Sie fragen, wo Tastatureingaben denn ein Dateiendezeichen haben
sollen. Setzen Sie es mit der Tastenkombination [Strg]
-[D]
(bzw. [Strg]
-[Z]
in
Windows). Wenn Sie <STDIN>
im Listenkontext verwenden, wartet Perl bei der
Dateneingabe, bis Sie mit [Strg]
-[D]
bzw. [Strg]
-[Z]
sagen: »Hier ist das Dateiende«,
speichert Ihre Daten dann in eine Liste und geht weiter im Skript.
Wirklich sinnvoll ist dieses Verhalten von <STDIN>
in Listenkontext vor allem, wenn
Sie aus Dateien lesen, also auch wirklich ein Dateiende haben. Für Tastatureingaben
verwendet man normalerweise <STDIN>
in skalarem Kontext. Aber es ist wichtig, sich
auch für die Eingabe die Unterschiede zwischen skalarem und Listenkontext
klarzumachen. Sie sind, wie bei so vielem in Perl, erheblich.
In den Beispielen dieses Kapitels haben wir zur Ausgabe von Listen mit Schleifen
gearbeitet, die jedes Listenelement betrachtet und dann ausgegeben haben. Wenn Sie
die Elemente einer Liste allerdings gar nicht verändern, sondern wirklich nur ausgeben
möchten, gibt es einen leichteren Weg: Die print
-Funktion geht grundsätzlich davon
aus, dass ihre Argumente in Listenkontext stehen, und das können Sie ausnutzen.
Nehmen wir zum Beispiel eine simple Liste von 1 bis 9:
@liste = (1..9);
Wenn Sie diese Liste einfach mit print @liste
ausgeben, erhalten Sie folgende
Ausgabe:
123456789
Es wird kein Zeilenvorschub ans Ende gesetzt. Die Werte der Liste werden lediglich verkettet, nichts weiter.
Wenn Sie die Elemente nun aber durch Leerzeichen getrennt ausgeben wollen? Sie
könnten eine while-
oder foreach
-Schleife verwenden. Doch es geht auch einfacher
- durch Variableninterpolation. Gestern haben wir die Variableninterpolation für
Strings besprochen, durch die in einem String "dies ist der String $zaehler"
die
Variable $zaehler
durch ihren aktuellen Wert ersetzt wurde. Variableninterpolation
funktioniert auch mit Array- oder Hash-Variablen - die Inhalte des Arrays oder Hash
werden dabei nacheinander, getrennt durch ein Leerzeichen, ausgegeben. Setzen Sie
zum Beispiel @liste
und einen Zeilenvorschub in Anführungszeichen:
print "@liste\n";
Sie erhalten die Liste mit Leerzeichen zwischen den Elementen und einem Zeilenvorschub am Ende:
1 2 3 4 5 6 7 8 9
Auch die Ausgabe von Hash-Variablen funktioniert auf diese Art, nur würde der Hash
zuerst in seine Schlüssel und Werte zerlegt. Variableninterpolation von Listenvariablen
macht es sehr einfach, die Inhalte einer Liste auszugeben, ohne auf Schleifen
zurückgreifen zu müssen. Beachten Sie, dass Sie deswegen in einem double-quoted
String vor ein @
-Zeichen einen Backslash setzen müssen, damit Perl nicht glaubt, es
wäre der Beginn einer Arrayvariablen und nach diesem gar nicht existierenden Array
sucht (und sich dann beschwert, dass es keines findet). Perl-Warnungen geben Ihnen
Bescheid, wenn Ihnen dieser Fehler unterläuft.
Eine andere Möglichkeit, die Ausgabe von Listen zu steuern, besteht darin, spezielle globale Perl-Variablen für die Trennsymbole für Ausgabefelder (Output field separator) und Ausgabedatensätze (Output record separator) zu setzen. Mehr über diese speziellen Variablen erfahren Sie im Vertiefungsabschnitt.
Sie haben in dieser Lektion viel gelernt, doch es gibt noch einiges mehr über Arrays und Hashes, das ich noch nicht besprochen habe (wirklich!). Dieser Abschnitt faßt einige Eigenschaften von Listen, Arrays und Hashes zusammen, die ich im Hauptteil dieser Lektion nicht behandelt habe.
Im Array-Zugriffsausdruck $array[index]
ist der (bei 0 beginnende) Index
normalerweise die Position des Elements im Array. Sie können aber auch negative
Array-Indizes verwenden, wie hier:
$array[-1];
Negative Array-Indizes zählen vom Ende des Arrays zurück: Der Index -1
bezieht sich
auf die letzte Position im Array (genau wie $#array
), -2
auf die vorletzte und so weiter.
Sie können mit negativen Indizes genauso auf die Elemente zugreifen wie mit
positiven, obwohl es vielleicht aus Lesbarkeitsgründen die bessere Idee wäre, bei den
positiven zu bleiben.
Wir haben in dieser Lektion mit dem Bereichsoperator ..
Zahlenlisten erstellt. Einige
Eigenschaften von Bereichen habe ich Ihnen bis jetzt vorenthalten. So können Sie
Bereiche zum Beispiel auch mit Buchstaben, genauer gesagt mit ASCII-Zeichen
erzeugen - die Liste, die Sie erhalten, beginnt mit dem ersten Operanden, endet mit
dem zweiten und enthält dazwischen alle Zeichen, die in der ASCII-Tabelle zwischen
den beiden Operanden liegen. Das Ergebnis des Bereichs 'a .. z'
ist zum Beispiel
eine Liste mit 26 Elementen, nämlich den Kleinbuchstaben von a bis z .
Sie können auch Zahlen und Buchstaben oder mehrere Buchstaben kombinieren, der Bereichsoperator liefert Ihnen auf geradezu magische Art und Weise die Werte zwischen den höheren und niedrigeren Werten.
Außerdem kann der Bereichsoperator auch in skalarem Kontext verwendet werden.
Dann gibt er einen Booleschen Wert zurück, was in manchen Schleifen oder zur
»Simulation« von awk
- oder sed
-ähnlichem Verhalten nützlich sein kann. Sehen Sie in
der perlop-Manpage unter Range-Operator nach weiteren Informationen.
Die Funktionen zum Entfernen von Zeilenvorschubs- oder anderen Zeichen vom Ende
eines Strings, chomp
und chop
, akzeptieren auch eine Liste als Argument. In diesem
Fall arbeiten sie sich durch alle Listenelemente und entfernen jeweils den
Zeilenvorschub oder das letzte Zeichen von jedem Element. Das kann zum Beispiel
nützlich sein, wenn Sie alle Zeilenvorschübe aus Daten entfernen möchten, die Sie aus
einer Datei gelesen haben.
In der perlfunc-Manpage finden Sie weitere Informationen über chomp
und chop
.
In Perl ist ein Satz von globalen Spezialvariablen eingebaut, mit denen man Perls Verhalten in bestimmten Situationen steuern kann. Sie werden im Verlauf dieses Buchs viele dieser Variablen kennenlernen; eine vollständige Liste finden Sie auf der perlvar-Manpage.
Relevant für das heutige Thema sind die Variablen Output field separator (Trennsymbol für Ausgabefelder), Output record separator (Trennsymbol für Ausgabedatensätze) und List separator (Listentrennsymbol). Mit diesen drei globalen Variablen können Sie Perls Standardverhalten bei der Ausgabe von Listen verändern. Tabelle 4.1 erläutert diese Variablen.
Tabelle 4.1: Globale Ausgabevariablen
Wie Sie im Abschnitt »Listen ausgeben« gelernt haben, verkettet Perl alle Elemente einer Liste, wenn Sie die Liste ausgeben:
print (1,2,3); # gibt "123" aus
In Wirklichkeit setzt Perl den Wert des Ausgabefeld-Trennsymbols zwischen die Elemente und den des Ausgabedatensatz-Trennsymbols ans Ende der Liste. Da diese beiden Variablen standardmäßig leer sind, wird zwischen den Elementen und am Ende auch nichts ausgegeben - es sei denn, Sie verändern diese Variablen und legen zum Beispiel folgendes Ausgabeverhalten fest:
$, = '*';
$\ = "\n";
print (1,2,3); # gibt "1*2*3\n" aus
Das Listentrennsymbol bestimmt wie das Ausgabefeld-Trennsymbol aussieht, was zwischen Listenelementen ausgegeben wird, gilt aber nur für Listenvariablen in interpolierten Strings. Voreingestelltes Listentrennsymbol ist ein Leerzeichen - deswegen werden bei der Interpolation von Array- und Hash-Variablen standardmäßig Leerzeichen zwischen die Elemente gesetzt, wie Sie im Abschnitt »Listen ausgeben« gesehen haben. Möchten Sie dieses Ausgabeverhalten verändern, setzen Sie einfach das Listentrennsymbol auf einen Wert Ihrer Wahl.
Zusätzlich zu Listen-, skalarem und Booleschem Kontext kennt Perl auch einen leeren Kontext - das sind die Stellen, an denen Perl überhaupt nichts erwartet. Wenn Sie zum Beispiel einen String als Anweisung in Ihr Skript setzen:
"das bringt nichts";
dann bringt das wirklich nichts - die einzige Auswirkung ist (bei aktivierten Perl-
Warnungen) die Fehlermeldung useless use of a constant in void context
(sinnlose Verwendung einer Konstante in leerem Kontext). Eine Fehlermeldung zu
void context warnt Sie also, wenn Sie einen Ausdruck an einer Stelle verwenden, an
der Perl gar nichts erwartet.
Heute war Listentag. Wie Sie heute gelernt haben, ist eine Liste einfach eine in
Klammern gesetzte Reihe von durch Kommata getrennten Skalaren. Weisen Sie eine
Liste einer Arrayvariablen @array
zu, können Sie mit der Array-Zugriffssyntax
@array[index]
auf die einzelnen Elemente dieses Arrays zugreifen. Wir haben des
weiteren besprochen, wie man die letzte Position im Array ($#array
) und die Anzahl
der Elemente ($elements = @array
) herausfindet. Diesen Abschnitt haben wir mit ein
paar Bemerkungen zu Listensyntax und Listenzuweisungen abgeschlossen, mit denen
Sie Variablen auf beiden Seiten eines Zuweisungsausdrucks parallel Werte zuweisen
können.
Dann sind wir das Thema Kontext angegangen, das eine wichtige Rolle spielt, weil Perl viele Dinge im Skalarkontext anders auswertet als im Listenkontext. Sie haben sich mit drei Fragen zum Verstehen eines Kontexts befaßt: Welcher Kontext wird in einem Ausdruck erwartet, welchen Datentyp haben die Daten, und was machen diese Daten in diesem Kontext?
Schließlich haben wir uns weiter mit einfacher Eingabe/Ausgabe, heute in bezug auf Listen, befaßt. Morgen werden wir Ihr Listengrundwissen mit der Behandlung von Hashes vervollständigen. (An Tag 19 werden wir noch fortgeschrittenere Datenstrukturen betrachten und dazu noch einmal auf die Listen zurückkommen - aber mit Listen, Arrays und Hashes im Perl-Repertoire können Sie schon eine Menge anstellen.)
Im folgenden noch einmal die Perl-Funktionen, die Sie heute kennengelernt haben:
qw
nimmt eine Liste von durch Leerzeichen getrennten Strings entgegen und gibt
eine Liste der einzelnen Stringelementen zurück. qw
erspart Ihnen das Eintippen
vieler Anführungszeichen und Kommata, wenn Sie eine lange Liste von Strings zu
definieren haben.
defined
nimmt eine Variable oder ein Listenelement entgegen und gibt wahr
zurück, wenn der Wert an dieser Stelle nicht der undefinierte ist.
undef
nimmt eine Variable oder ein Listenelement entgegen und setzt sie/es auf
den undefinierten Wert. Ohne Argumente gibt undef
nur den undefinierten Wert
zurück.
sort
nimmt eine Liste als Argument entgegen, sortiert diese Liste in ASCII-
Reihenfolge und gibt die sortierte Liste zurück. Setzt man die Anweisung { $a <=>
$b }
vor die Liste, sortiert es in numerischer Reihenfolge.
scalar
wertet eine Liste in Skalarkontext aus.
Mehr Informationen finden Sie in der perlfunc-Manpage bzw. im Anhang A.
Frage:
Was ist der Unterschied zwischen dem undefinierten Wert und undef
?
Antwort:
Der Unterschied ist gering. Der undefinierte Wert ist der Wert, den Perl
Variablen, Array-Elementen oder Hash-Werten zuweist, wenn für sie kein
Wert vorhanden ist - wenn Sie eine Variable verwenden, ohne sie zu
initialisieren, oder wenn Sie die Länge eines Arrays vergrößern, ohne
entsprechend Elemente einzufügen. Möchten Sie den undefinierten Wert
explizit verwenden, zum Beispiel um eine Variable undefiniert zu machen
oder den undefinierten Wert in ein Array aufzunehmen, nehmen Sie dafür
die Funktion undef
. Wegen der engen Beziehung zwischen dem undefinierten
Wert und undef
ist es weit verbreitet, dass undef
für den undefinierten Wert
steht (wie in »Die letzten drei Elemente dieses Arrays sind undef
«). Sie werden
in Ihrem Code nichts falsch machen, wenn Sie undef
überall dort verwenden,
wo Sie einen undefinierten Wert haben möchten.
Frage:
Ich möchte ein Array von Arrays erstellen.
Antwort:
Das können Sie nicht. Besser gesagt, jetzt noch nicht. Für Arrays von Arrays
oder Arrays von Hashes oder jede Art von verschachtelten Datenstrukturen
brauchen Sie Referenzen - und etwas Geduld. Wir werden Referenzen an Tag
19 behandeln.
Frage:
Oh je! Ich verstehe Skalar- und Listenkontext nicht. Wenn verschiedene
Operationen verschiedene Dinge machen können und es keine Regeln gibt, wie
Listen und Skalare sich im jeweils anderen Kontext verhalten, heißt das, dass ich
mir für jede Operation merken muss, was sie in jedem Kontext macht?
Antwort:
Äh, ja. Nein. Naja, ungefähr. Wenn Sie die Frage nach dem Kontext absolut
abscheulich finden, können Sie die esoterischeren
Kontexterscheinungsformen in so gut wie jedem Skript weitestgehend
vermeiden, merken Sie sich nur die wichtigsten Besonderheiten (wie man zum
Beispiel die Länge eines Arrays herausfindet), und schlagen Sie den Rest
nach, wenn irgend etwas nicht richtig funktioniert. Wenn Sie allerdings
Skripts anderer Programmierer lesen, müssen Sie vermutlich auf den Kontext
achten oder sogar nach verborgenem Kontext suchen.
Frage:
Ich möchte die Anzahl der Elemente in einer Liste herausfinden, also habe ich
geschrieben »length @array...
«
Antwort:
Hören Sie gleich hier auf. Die length
-Funktion ist eine nette Funktion - für
Strings und Zahlen. Um die Anzahl der Elemente in einer Liste
herauszufinden, sollten Sie die Arrayvariable in skalarem Kontext auswerten:
$anzahl_elemente = @array
.
Frage:
Wie durchsuche ich ein Array nach einem bestimmten Element?
Antwort:
Eine Möglichkeit wäre, das Array mit einer foreach-
oder while
-Schleife zu
durchlaufen und nacheinander jedes Element zu überprüfen. Perl hat aber
auch eine Funktion namens grep
(nach dem Unix-Suchbefehl), die das für Sie
übernimmt. Am Tag 8 werden Sie mehr über grep
erfahren.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.
@liste = (1, (), (4, 3), $foo, ((), 10, 5 + 4), (), (''));
($x, $y, $z) = ('a', 'b');
($u, @mehr, $v) = (1 .. 10);
$zahlen[4] = (1, 2, 3); # @zahlen enthaelt anfangs (10,9,8)
undef $zahlen[4];
$zahlen[$#zahlen];
$foo = @zahlen;
@mehr = 4;
<STDIN>
in skalarem und in Listenkontext? Warum
ist das wichtig?
print 'Geben Sie eine Zahlenliste ein: ';
chomp($in = <STDIN>);
@zahlen = split(" ", $in);
@sortiert = sort @zahlen;
print "eingegebene Zahlen: @sortiert\n";
$anz_zahlen = $#zahlen;
print "Anzahl eingegebener Zahlen: $anz_zahlen\n";
Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.
(1, 4, 3, $foo, 10, 9, '')
. Dabei wird für $foo
der
aktuelle Wert von $foo
eingesetzt.
$x
wird 'a'
, $y
wird 'b'
, $z
wird undefiniert. Zuweisungen an Listen auf der
linken Seite finden parallel statt, die Werte auf der rechten Seite werden den
korrespondierenden Variablen auf der linken Seite zugewiesen.
$u
wird 1
, @mehr
wird (2,3,4,5,6,7,8,9,10)
, $v
wird undefiniert.
Arrayvariablen auf der linken Seite einer Listenzuweisung sind gefräßig: Sie
»schlucken« alle verbleibenden Werte auf der rechten Seite.
$zahlen[4]
wird 3
. Beim Zuweisen einer rohen Liste an einen Skalar weist Perl
nur den letzten Wert der Liste zu und ignoriert alle vorigen Werte.
@zahlen
(10,9,8)
war, ist der neue Wert von
@zahlen
jetzt (10,9,8,undef,3)
. Wie gesagt werden bei Zuweisung von
Listensyntax in skalarem Kontext alle Werte außer dem letzten ignoriert.
$zahlen[4]
wird auf den undefinierten Wert gesetzt (vorher war es 3
).
$zahlen[$#zahlen]
verweist auf den Wert an der letzten Position der Liste.
$foo
enthält nach dieser Zuweisung die Anzahl der Elemente im Array @zahlen
.
4
wird zu einer Liste »befördert«, und @mehr
wird eine Liste mit einem
Element: (4)
.
sort
-Funktion:
@sortiert = sort @array;
<STDIN>
eine Eingabezeile ein, die dann zu Ende ist,
wenn der Benutzer die Eingabetaste drückt, und speichert diese Zeile in einer
Skalarvariablen. In einem Listenkontext liest <STDIN>
alle Zeilen von der
Standardeingabe, bis es auf ein Dateiendezeichen stößt, und speichert jede Zeile
als ein Element in einer Liste. Das sind zwei ganz verschiedene Arten, Daten in Ihr
Programm einzulesen.
#!/usr/bin/perl -w
$eins = 0;
$zwei = 0;
print 'Geben Sie eine Bereichsgrenze ein: ';
chomp ($eins = <STDIN>);
print 'Geben Sie die andere Bereichsgrenze ein: ';
chomp ($zwei = <STDIN>);
if ($eins < $zwei) {
@array = ($eins .. $zwei);
} else {
@array = ($zwei .. $eins);
}
print "@array \n"
foreach
-Schleifen Gebrauch macht:
#!/usr/bin/perl -w
$input = '' ; # Zwischenspeicher für Eingaben
@array1 = (); # erstes Array
@array2 = (); # zweites Array
@final = (); # Schnittmenge
print 'Geben Sie das erste Array ein: ';
chomp($input = <STDIN>);
@array1 = split(' ', $input);
print 'Geben Sie das zweite Array ein: ';
chomp($input = <STDIN>);
@array2 = split(' ', $input);
foreach $el (@array1) {
foreach $el2 (@array2) {
if (defined $el2 && $el eq $el2) {
$final[$#final+1] = $el;
undef $el2;
last;
}
}
}
print "@final\n";
$el
dem letzten Element im Array zuweist, könnten Sie auch
die push
-Funktion verwenden, die genau das gleiche tut (aber etwas einfacher zu
lesen ist):
push @final $el;
$#zahlen
die Anzahl der Elementen sei, ist falsch ($#zahlen
liefert
den höchsten Index, und der ist um eins kleiner als die Anzahl von Elementen).
Verwenden Sie statt dessen:
$anz_zahlen = @zahlen
$anz_zahlen = $#zahlen + 1.